package com.uottawa.librarytest.service;

import static org.junit.Assert.*;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.uottawa.library.dao.BaseDAO;
import com.uottawa.library.dao.ItemDAO;
import com.uottawa.library.dao.LoanDAO;
import com.uottawa.library.dao.UserDAO;
import com.uottawa.library.domain.Item;
import com.uottawa.library.domain.Loan;
import com.uottawa.library.domain.LoanPeriod;
import com.uottawa.library.domain.User;
import com.uottawa.library.service.ItemService;
import com.uottawa.library.service.LoanPeriodService;
import com.uottawa.library.service.LoanService;
import com.uottawa.library.service.TitleService;
import com.uottawa.library.service.UserService;

public class LoanServiceTest {

	private LoanService service;
	private User adminUser;
	private LoanPeriodService loanPeriodService;
	private LoanService loanService;
	private UserDAO userDao;
	private UserService userService;
	private LoanDAO loanDAO;
	private TitleService titleService;
	private ItemService itemService;
	private ItemDAO itemDAO;



	@Before
	public void setup() {
		service = new LoanService();
		adminUser = new User(13243, "admin", "password", 0, "admin");
		
		LoanPeriod loanPeriod = new LoanPeriod(1, 5); // initial loan period duration
		LoanPeriod renewal1 = new LoanPeriod(2,10); // first renewal
		LoanPeriod renewal2 = new LoanPeriod(3,10); // Second renewal
		
		loanPeriodService = new LoanPeriodService();  //
		loanPeriodService.addPeriod(loanPeriod, adminUser);
		loanPeriodService.addPeriod(renewal1, adminUser);
		loanPeriodService.addPeriod(renewal2, adminUser);
		
		loanService = new LoanService();
		userDao = new UserDAO();
		userService = new UserService();
		loanDAO = new LoanDAO();
		
		titleService = new TitleService();
		itemService = new ItemService();
		itemDAO = new ItemDAO();
	}

	@After
	public void cleanup() {
		new BaseDAO().cleanUp();
	}
	
	@Test
	public void testAuthorizeLoanSuccess() {
		// create user with 0 balance
		User user = new User(11111, "OkUser", "password", 0, "user");
		userDao.addUser(user);

		Item item = new Item();
		item.setItemId(6666);

		// insert loan. expect success
		String result = service.borrowLoanCopy(item, user);
		assertEquals("success", result);
	}

	@Test
	public void testRejectLoanForOutstandingBalance() {
		// create user with outstanding balance
		User user = new User(11111, "OkUser", "password", 0, "user");
		userDao.addUser(user);
		// retrieve user from DB to get the auto generated id.
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(6666);

		loanService.borrowLoanCopy(item, user);
		loanService.chargeFees(user.getUserId(), 6666, 10.00);
		item = new Item();
		item.setItemId(77777);

		// Create loan . expect rejection.
		String result = service.borrowLoanCopy(item, user);
		assertEquals("rejected ! you have an outstanding balance", result);
	}
	
	@Test
	public void allowLoanAfterPayingFees(){
		User user = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user);
		
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(3333);
		item.setIsbn(4444);
		
		// create a loan with late fees.
		loanService.borrowLoanCopy(item, user);
		
		// charge late fees.
		loanService.chargeFees(user.getUserId(), 3333, 10.5);
		
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());
		loanService.returnLoan(loan);
		
		// trying borrowing an item. expect rejection.
		String result = service.borrowLoanCopy(item, user);
		assertEquals("rejected ! you have an outstanding balance", result);
		
		// pay late fees.
		loanService.payFees(loan.getLoanId());
		
		// trying borrowing again. expect success this time.
		result = service.borrowLoanCopy(item, user);
		assertEquals("success", result);
		
	}
	@Test
	public void instantanousReturn() {
		/*
		 * Scenario: 
		 * 1- user returns before the expire date 
		 * 2-return should not have a cost (no fee for the user)
		 */
			
		User user = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user);
		// retrieve user from DB to get the auto generated id.
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(3333);

		loanService.borrowLoanCopy(item, user);

		// get the loan after database insertion.
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());
		assertEquals("success", loanService.returnLoan(loan));
	}
	
	@Test
	public void userReturnItemThenOtherUserBorrowSameItem() {
	
		
		User user1 = new User(0, "OkUser", "password", 0, "user");
		User user2 = new User(120, "OkUser2", "password", 0, "user");
		userService.addUser(user1);
		userService.addUser(user2);
		// retrieve user from DB to get the auto generated id.
		user1 = userDao.getUser("OkUser", "password");
		user1 = userDao.getUser("OkUser2", "password");
		Item item = new Item();
		item.setItemId(3333);

		loanService.borrowLoanCopy(item, user1);

		// get the loan after database insertion.
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());
		loanService.returnLoan(loan);
		assertEquals("success", loanService.borrowLoanCopy(item, user2));
	}
	
	@Test
	public void returnWithLateFees(){
		User user = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user);
		
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(3333);
		item.setIsbn(4444);
		
		loanService.borrowLoanCopy(item, user);
		
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());

		loan.setLateFees(10.5);
		loanService.updateLoan(loan);
		assertEquals("success", loanService.returnLoan(loan));
	}
	@Test
	public void userReturen_afterExpiryDate()
	{	
		User regularUser = new User(623, "UserWithExpiryDate", "password", 0, "user");
		userDao.addUser(regularUser);
		regularUser = userDao.getUser("UserWithExpiryDate", "password");

		Item item = new Item();
		item.setItemId(1020);
		loanService.borrowLoanCopy(item, regularUser);
		
		Loan loan = loanDAO.getLoanByItemId(item.getItemId());
	
		java.util.Date currentDate = Calendar.getInstance().getTime(); // current date of now 
		currentDate.setHours(currentDate.getHours() - 3 );
		loan.setExpiryDate(new java.sql.Timestamp(currentDate.getTime()));
		loanDAO.updateLoan(loan);
		
		String result = loanService.returnLoan(loan);
		
		assertEquals("success", result);

	
		
		
	}
	@Test
	public void userReturen_asExpiryDate()
	{	
		User regularUser = new User(623, "UserWithExpiryDate", "password", 0, "user");
		userDao.addUser(regularUser);
		regularUser = userDao.getUser("UserWithExpiryDate", "password");

		Item item = new Item();
		item.setItemId(1020);
		loanService.borrowLoanCopy(item, regularUser);
		
		Loan loan = loanDAO.getLoanByItemId(item.getItemId());
	
		java.util.Date currentDate = Calendar.getInstance().getTime(); // current date of now 
		currentDate.setSeconds(currentDate.getSeconds() - 2 );
		loan.setExpiryDate(new java.sql.Timestamp(currentDate.getTime()));
		loanDAO.updateLoan(loan);
		
		String result = loanService.returnLoan(loan);
		
		assertEquals("success", result);

	
		
		
	}
	
	

	@Test
	public void checkLateFeesNotifcationForUser() {
		/**
		 * Create couple of expired loans for a user. call the check
		 * Notification method to check the expired loans. assert that method
		 * actually returns a notification.
		 */

		User user = new User(0, "OkUser", "password", 0, "user");
		userDao.addUser(user);
		// retrieve user from DB to get the auto generated id.
		user = userDao.getUser("OkUser", "password");
		
		Item item = new Item();
		item.setItemId(3333);

		loanService.borrowLoanCopy(item, user);
		loanService.chargeFees(user.getUserId(), item.getItemId(), 5);
		
		assertFalse(loanService.checkNotification(user));

	}

	@Test
	public void checkForNoLateFeesNotifcation() {
		
		User user = new User(0, "OkUser", "password", 0, "user");
		userDao.addUser(user);
		// retrieve user from DB to get the auto generated id.
		user = userDao.getUser("OkUser", "password");
		
		Item item = new Item();
		item.setItemId(3333);

		loanService.borrowLoanCopy(item, user);
		loanService.chargeFees(user.getUserId(), item.getItemId(), 0);
		
		assertTrue(loanService.checkNotification(user));
	}

	@Test
	public void oneUserOnlyCanBorrowCopy()
	{
		// two users want to borrow the same item
		Item item1 = new Item();
		item1.setIsbn(232424);
		item1.setItemId(7654);
		
		/*
		ItemDAO itemDAO = new ItemDAO();
		itemDAO.addItem(item1);
		*/
		
		User user1 = new User(43, "User1", "password", 0, "user");
		userDao.addUser(user1);
		
		User user2 = new User(28, "User2", "password", 0, "user");
		userDao.addUser(user2);
		
		User user3 = new User(2823, "User3", "password", 0, "user");
		userDao.addUser(user3);
		

		loanService.borrowLoanCopy(item1, user1);
		
		loanService.borrowLoanCopy(item1, user2);
		
		Loan loan1 = loanDAO.getLoanByItemId(item1.getItemId());
		boolean user = (loan1.getUserId() == user1.getUserId());
		
		assertTrue(user);

	}

	@Test
	public void threeUsersBorrowSameTitle(){
		User user1 = new User(0, "user1", "user1pass", 0, "user");
		User user2 = new User(1, "user2", "user1pass", 0, "user");
		User user3 = new User(2, "user3", "user1pass", 0, "user");
		titleService.addTitle(1752123, "Catcher in the Rye");
		titleService.addTitle(1752124, "The Little Prince");	
		titleService.addTitle(1752125, "The Destiny");
		itemService.addItem(1752123);
		Item item1 = new Item();
		item1.setIsbn(1752123);
		item1.setItemId(0);
		Item item2 = new Item();
		item2.setIsbn(1752123);
		item2.setItemId(1);
		Item item3 = new Item();
		item3.setIsbn(1752123);
		item3.setItemId(2);
		
		String loan1 = loanService.borrowLoanCopy(item1, user1);
		String loan2 = loanService.borrowLoanCopy(item2, user2);
		String loan3 = loanService.borrowLoanCopy(item3, user3);
		
		Boolean equal = false;
		
		if (loan1.equals("success") && loan2.equals("success") && loan3.equals("success")){
			equal = true;
		}
		
		assertTrue(equal);
	}
	
	
	@Test
	public void oneUserBorrowSameCopy()
	{
		// two users want to borrow the same item
		Item item1 = new Item();
		item1.setIsbn(232424);
		item1.setItemId(7654);
		
		
		User user1 = new User(43, "User1", "password", 0, "user");
		userDao.addUser(user1);
		
		String responserForFirstBorrowAction = loanService.borrowLoanCopy(item1, user1);
		String responserForSecondBorrowAction = loanService.borrowLoanCopy(item1, user1);
		
		
		assertEquals("success",responserForFirstBorrowAction);
		assertEquals("rejected! item is already borrowed",responserForSecondBorrowAction);
	}
		
	@Test 
	public void twoCopiesThreeUsers(){
			User user1 = new User(10, "username1", "password1", 0, "user");
			User user2 = new User(11, "username2", "password2", 0, "user");
			User user3 = new User(12, "username3", "password3", 0, "user");
			
		userDao.addUser(user1);
		userDao.addUser(user2);
		userDao.addUser(user3);
		
		Item item1 = new Item();
		item1.setItemId(1);
		item1.setIsbn(0);
		Item item2 = new Item();
		item1.setItemId(2);
		item2.setIsbn(0);
		
		loanService.borrowLoanCopy(item1, user1);
		loanService.borrowLoanCopy(item2, user2);
		loanService.borrowLoanCopy(item2, user3);
		
		Loan loan1 = loanDAO.getLoanByItemId(item2.getItemId());
		boolean checkLoan = (loan1.getUserId() == user3.getUserId());
		
		assertFalse(checkLoan);
		
	}
	
	
	@Test
	public void returnAndPay(){
		User user = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user);
		
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(3333);
		item.setIsbn(4444);
		
		loanService.borrowLoanCopy(item, user);
		
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());

		loan.setLateFees(10.5);
		loanService.updateLoan(loan);
		loanService.returnLoan(loan);
		assertEquals("success", loanService.payFees(loan.getLoanId()));
	}
	
	@Test
	public void payFineAfterLoanExpires(){
		User user = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user);
		
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(3333);
		item.setIsbn(4444);
		itemDAO.addItem(item);
		loanService.borrowLoanCopy(item, user);
		
		// create timestamp in the past.
		Timestamp timestamp = new Timestamp(new Date().getTime() - 100000);
		Loan loan = new Loan();
		
		loan = loanDAO.getLoanByItemId(item.getItemId());
		
		// expire and charge fees for loan
		loan.setExpiryDate(timestamp);
		loanService.updateLoan(loan);
		loanService.chargeFees(user.getUserId(), 3333, 10.5);
		
		// return loan
		loanService.returnLoan(loan);
		assertEquals("success", loanService.payFees(loan.getLoanId()));
	}
	
	@Test
	public void payFineAfterRenewalExpires(){
		User user = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user);
		
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(3333);
		item.setIsbn(4444);
		
		loanService.borrowLoanCopy(item, user);
		
		// create timestamp in the past.
		Timestamp timestamp = new Timestamp(new Date().getTime() - 100000);
		Loan loan = new Loan();
		
		// renew loan.
		loan = loanDAO.getLoanByItemId(item.getItemId());
		loanService.renewLoan(loan.getLoanId(), user);
		
		//expire the loan and charge fees.
		loan.setExpiryDate(timestamp);
		loanService.updateLoan(loan);
		loanService.chargeFees(user.getUserId(), 3333, 10.5);

		// return the loan.
		loanService.returnLoan(loan);
		
		assertEquals("success", loanService.payFees(loan.getLoanId()));
	}
	
	@Test
	public void payFineWithoutReturn(){
		User user = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user);
		
		user = userDao.getUser("OkUser", "password");
		Item item = new Item();
		item.setItemId(3333);
		item.setIsbn(4444);
		
		loanService.borrowLoanCopy(item, user);
		
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());

		loanService.chargeFees(user.getUserId(), 3333, 10.5);
		
		assertEquals("failed! Please return the item first", loanService.payFees(loan.getLoanId()));
	}
	
	@Test
	public void removeItem(){
		User user1 = new User (0, "user1", "user1pass", 0, "user");
		titleService.addTitle(1752123, "Catcher in the Rye");
		itemService.addItem(1752123);
		assertEquals("success", itemService.removeItem(1752123));
	}
	
	@Test
	public void removeReturnedItem(){
		User user1 = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user1);
		user1 = userDao.getUser("OkUser", "password");
		titleService.addTitle(1752123, "Catcher in the Rye");
		Item item = new Item();
		item.setItemId(1);
		item.setIsbn(1752123);
		itemDAO.addItem(item);
		loanService.borrowLoanCopy(item, user1);
		
		
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());
		loanService.returnLoan(loan);

		assertEquals("success", itemService.removeItem(1752123));
	}
	
	@Test
	public void removeNotReturnedItem(){
		User user1 = new User(0, "OkUser", "password", 0, "user");
		userService.addUser(user1);
		user1 = userDao.getUser("OkUser", "password");
		titleService.addTitle(1752123, "Catcher in the Rye");
		Item item = new Item();
		item.setItemId(1);
		item.setIsbn(1752123);
		itemDAO.addItem(item);
		loanService.borrowLoanCopy(item, user1);
		
		
		Loan loan = new Loan();
		loan = loanDAO.getLoanByItemId(item.getItemId());
		//loanService.returnLoan(loan);

		assertEquals("failed", itemService.removeItem(1));
	}
	
	
}
